#include "midi.h"
#include <QDebug>
#include <vector>
midi::midi()
{
}

midi::midi(QString filepath)
{
    FILE *midifile;
    if((midifile = fopen(filepath.toLocal8Bit(),"rb"))==NULL)
    {
        //QDebug << "cannot open file:"<<filepath;
        return;
    }
    unsigned char mtrd[14];
    int fFormat;
    int tempo;
    //read head chunk
    fread(mtrd,1,14,midifile);
    fFormat = mtrd[9];
    int trackNum = mtrd[11];
    tempo =  mtrd[12]<<8 + mtrd[13];
    dtempo=tempo;
    //read mtrk
    vector<unsigned long>size;
    unsigned char* trks[trackNum];
    for(int i=0;i<trackNum;i++)
    {
        unsigned char *temp = (unsigned char*)malloc(4);
        unsigned char buf[4];
        fread(temp,1,4,midifile);
        fread(buf,1,4,midifile);
        size.push_back(setSize(buf));

        trks[i]=(unsigned char*) malloc (size[i]);
        fread(trks[i],1,size[i],midifile);

    }
    fclose(midifile);
    getTempo(trks[0],size[0]);

    for(int i=1;i<trackNum;i++)
    {
        midiTrack.push_back(getMidiMsg(trks[i],size[i]));
    }

}

void midi::getTempo(unsigned char *trk, long tsize)
{
    long time=0;
    long trkpos=0;
    tempo tp;

    while(trkpos<tsize)
    {
        time+= getDTime(trk,trkpos);
        unsigned char Etype = trk[trkpos];
        trkpos++;
        if(Etype==0xff)
        {

            unsigned char metaType;
            unsigned char metaLen;
            metaType = trk[trkpos];
            trkpos++;
            metaLen = getValue(trk,trkpos);
            if(metaType == 0x51)
            {

                unsigned char buf[3];
                for(int i=0;i<3;i++,trkpos++)
                {
                    buf[i]= trk[trkpos];
                }
                tp.tick=((buf[0]<<16) | (buf[1]<<8) | buf[2]);
                tp.deltaTime =time;
                tempoMap.push_back(tp);
            }
            else
            {
                trkpos+=metaLen;
                if(trkpos<tsize)
                {
                    time+= getDTime(trk,trkpos);
                }
            }
        }
    }
}

track midi::getMidiMsg(unsigned char* trk, long tsize)
{
    long tempoTime=0;
    long time=0;
    track trkMsg;
    long trkpos=0;
    note preNote;
    note cNote;
    int tid=0;
    while(trkpos < tsize)
    {
        if(tempoTime >tempoMap[tid].deltaTime)
        {

               //tid++;
        }
        time = getDTime(trk,trkpos);
        unsigned char Etype = trk[trkpos];
        trkpos++;
        tempoTime+=time;

        if(Etype==0xff)
        {
            unsigned char metaType;
            unsigned char metaLen;
            metaType = trk[trkpos];
            trkpos++;
            metaLen = getValue(trk,trkpos);
            int cPos=trkpos;
            trkpos+=metaLen;
            info tempinfo;
            tempinfo.type = metaType;
            for(int j=0;j<metaLen;j++)
            {
                tempinfo.detail.push_back(trk[cPos+j]);
            }
            if(time==0)
            {
                trkMsg.trkinfos.push_back(tempinfo);
            }else
            {
                note newnote;
                newnote.message=0x00003c00;
                newnote.deltaTime=time*tempoMap[tid].tick/500000;
                //trkMsg.trknotes.push_back(newnote);
            }

            continue;
        }
        else if(Etype ==0xf0||Etype==0xf7)
        {
            trkpos+=getValue(trk,trkpos);
            continue;
        }
        else if(Etype >=0x80 && Etype <0xf0)
        {
            note newnote;
            newnote.deltaTime=time*tempoMap[tid].tick/500000;
            tempoTime+= time;
            if(Etype>=0xc0 && Etype<0xd0)
            {
                unsigned char buf[2];
                buf[0]=Etype;
                buf[1]=trk[trkpos];
                trkpos++;
                newnote.message=(buf[1]>>8)+buf[0];
                trkMsg.trknotes.push_back(newnote);
                preNote=newnote;
                continue;
            }
            unsigned char buf[3];
            buf[0]=Etype;
            buf[1]=trk[trkpos];
            trkpos++;
            buf[2]=trk[trkpos];
            trkpos++;
            newnote.message=(buf[2]<<16)+(buf[1]<<8)+buf[0];
            trkMsg.trknotes.push_back(newnote);
            preNote=newnote;
            continue;
        }
        else if(Etype<0x80)
        {
            note newnote;
            newnote.deltaTime=time*tempoMap[tid].tick/500000;
            unsigned char buf[2];
            buf[0]=Etype;
            buf[1]=trk[trkpos];
            trkpos++;
            newnote.message=preNote.message &0xFF;
            newnote.message=newnote.message+((buf[1]<<16)|(buf[0]<<8));
            trkMsg.trknotes.push_back(newnote);
            preNote=newnote;
            continue;
        }
    }
    return trkMsg;
}




unsigned long midi::setSize(unsigned char buf[])
{
    long a= (buf[0]<<24) | (buf[1]<<16)| (buf[2]<<8) | (buf[3]);
    return a;
}
unsigned long midi::getValue(unsigned char* track,long &trkpos)
{
    register unsigned long value;
    register unsigned char c;
    value = track[trkpos];
    trkpos++;
    if ( value & 0x80 )
    {
        value &= 0x7F;
        do
        {
            c=track[trkpos];
            trkpos++;
            value = (value << 7) + (c & 0x7F);

        } while (c & 0x80);
    }


    return(value);
}
unsigned long midi::getDTime(unsigned char *track, long &trkpos)
{
    return getValue(track,trkpos);
}

